package cbs.jdtag;

import java.awt.Image;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Locale;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;


/**
 * This is the applications (static) context.
 * It is accessible from everywhere.
 * 
 */
public final class AppContext {

	private static transient final Logger LOGGER = Logger.getLogger(AppContext.class.getName());
	
	private static final File CONFIG_DIR	= new File(System.getProperty("user.home")	+ "/.jdtag");
	private static final String CONFIG_FILENAME = "jdtag.properties";
	private static final String LOGGING_FILENAME = "logging.properties";
	private static final String ICON_FILENAME = "critt_logo.jpg";
	
	// with '/', as they are read from the file system
	// would be without '/' if the file should be loaded from the ResourceBundle through the class loader
	private static final File CONFIG_FILE	= new File(CONFIG_DIR.getAbsolutePath()	+ "/" + CONFIG_FILENAME);
//	private static final File LOGGING_FILE	= new File(CONFIG_DIR.getAbsolutePath() + "/" + LOGGING_FILENAME);
	private static final File LOGGING_FILE	= new File("/" + LOGGING_FILENAME);
	private static final String ICON_FILE	= "/" + ICON_FILENAME;
	
	private static final String USERNAME_KEY		= "username";
	private static final String USERLOCALE_KEY		= "userLocale";
	private static final String RECENTLYUSEDDIR_KEY		= "recentlyUsedDir";
	private static final String SHOWBOXES_KEY		= "showBoxes";
	private static final String ENCODING_KEY		= "encoding";
	
	private static ImageIcon icon;
	private static Properties defaultConfig = new Properties();
	private static Properties userConfig;
	
	private static String recentlyUsedDir = System.getProperty("user.home");
	private static String encoding = "ISO-8859-15";
	
	private static DataModel2 dm = new DataModel2();
	public static MainApp mainView = null;
	
	/**
	 * This class is a singleton.
	 */
	private AppContext() { }
	
	/**
	 * Initializes the application.
	 * 
	 * @param mainFrame a reference to the main frame
	 */
	public static void initialize(final String mainFrame) {
		startup();
		
		// the correct way to start a Java Swing App
		// (see http://java.sun.com/developer/technicalArticles/javase/swingworker/index.html)
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				try {
					AppContext.mainView = (MainApp) Class.forName(mainFrame).newInstance();
					// can be used for debugging
					// ((Component) AppContext.mainFrame).setDebugGraphicsOptions(DebugGraphics.FLASH_OPTION | DebugGraphics.LOG_OPTION);
					AppContext.mainView.setVisible(true);
					AppContext.mainView.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				}
				catch (Exception e) {
					LOGGER.log(Level.SEVERE, "Could not initialize main frame.", e);
				}
			}
		});
	}
	
	/**
	 * Reads the config files and initializes the
	 * application context with the values stored there.
	 * If the files/folders do not exist, they are created.
	 * 
	 */
	private static void startup() {

		icon = new ImageIcon(AppContext.class.getResource(ICON_FILE));
		
		if (!CONFIG_DIR.exists()) {
			if (!CONFIG_DIR.mkdirs()) {
				LOGGER.warning("Could not create user configuration directory.");
			}
		}
		
		// to override this, use '-Djava.util.logging.config.file=<path/to/file>'
		if (System.getProperty("java.util.logging.config.file") == null) {
		
			try {
				if (LOGGING_FILE.exists()) {
					LogManager.getLogManager().readConfiguration(new FileInputStream(LOGGING_FILE));
				}
				else {
					final ClassLoader cl = AppContext.class.getClassLoader();
					LogManager.getLogManager().readConfiguration(cl.getResourceAsStream(LOGGING_FILENAME));
				}
			}
			catch (final SecurityException se) {
				LOGGER.warning("Could not load application logging configuration (security error), using system logging configuration.");
			}
			catch (final IOException ioe) {
				LOGGER.warning("Could not load application logging configuration (i/o error), using system logging configuration.");
			}
		}
		
		defaultConfig.setProperty(USERNAME_KEY, System.getProperty("user.name"));
		defaultConfig.setProperty(USERLOCALE_KEY, Locale.getDefault().toString());
		defaultConfig.setProperty(RECENTLYUSEDDIR_KEY, recentlyUsedDir);
//		defaultConfig.setProperty(SHOWBOXES_KEY, String.valueOf(dataModel.isShowBoxes()));
		defaultConfig.setProperty(ENCODING_KEY, encoding);
		
		userConfig = new Properties(defaultConfig);
		
		if (CONFIG_FILE.exists()) {
			LOGGER.fine("Loading custom user configuration...");
			
			FileInputStream fis = null;
			try {
				fis = new FileInputStream(CONFIG_FILE);
				userConfig.load(fis);
			}
			catch (final Exception e) {
				final String errorMsg = "User configuration file exists but could not be loaded.";
				LOGGER.log(Level.SEVERE, errorMsg, e);
				throw new RuntimeException(errorMsg);
			}
			finally {
				try { fis.close(); }
				catch (IOException e) { }
			}
		}

//		dataModel.setShowArrows(Boolean.valueOf(userConfig.getProperty(SHOWARROWS_KEY).toString()));
//		dataModel.setShowBoxes(Boolean.valueOf(userConfig.getProperty(SHOWBOXES_KEY).toString()));
		
		dm.setEncoding(userConfig.getProperty(ENCODING_KEY));
		
		LOGGER.log(Level.FINE, "Username is set to \"{0}\".", userConfig.getProperty(USERNAME_KEY));
		LOGGER.log(Level.FINE, "User locale is set to \"{0}\".", userConfig.getProperty(USERLOCALE_KEY));
		LOGGER.log(Level.FINE, "Recently used directory is set to \"{0}\".", userConfig.getProperty(RECENTLYUSEDDIR_KEY));
		LOGGER.log(Level.FINE, "Show boxes is set to \"{0}\".", userConfig.getProperty(SHOWBOXES_KEY));
		LOGGER.log(Level.FINE, "Encoding is set to \"{0}\".", userConfig.getProperty(ENCODING_KEY));
		
		LOGGER.info("Application startup completed.");
	}
	
	/**
	 * Should(!) be called through the window closing handler
	 * so that config values can be saved.
	 * 
	 */
	public static void shutdown() {
		FileOutputStream fos = null;
		
		try {
			userConfig.setProperty(ENCODING_KEY, dm.getEncoding());
			fos = new FileOutputStream(CONFIG_FILE);
			userConfig.store(fos, null);
		}
		catch (final IOException e) {
			final String errorMsg = "Could not save custom user configuration.";
			LOGGER.log(Level.SEVERE, errorMsg, e);
		}
		finally {
			try { fos.close(); }
			catch (IOException e) {	}
		}
		
		LOGGER.info("Application shutdown completed.");
	}
	
	/**
	 * Returns the path to the config directory.
	 * 
	 * @return the path to the config directory
	 */
	public static File getConfigDir() {
		return CONFIG_DIR;
	}
	
	public static String getRecentlyUsedDir() {
		return userConfig.getProperty(RECENTLYUSEDDIR_KEY);
	}
	
	public static void setRecentlyUsedDir(final String recentlyUsedDir) {
		userConfig.setProperty(RECENTLYUSEDDIR_KEY, recentlyUsedDir);
		LOGGER.log(Level.INFO, "Recently used directory is now set to \"{0}\".", recentlyUsedDir);
	}
	
	public static Image getIcon() {
		return icon.getImage();
	}
	
	public static void setShowBoxes(final boolean showBoxes) {
		userConfig.setProperty(SHOWBOXES_KEY, String.valueOf(showBoxes));
	}
	
	public static DataModel2 getDM() {
		return dm;
	}
}
